CloudFormation StackSetsのインポートが失敗するときの対処法

CloudFormation StackSetsのインポートが失敗するときの対処法

Clock Icon2024.07.19

こんにちは。たかやまです。

CloudFormation StackSetsでは既存スタックをStackSetsにインポートする機能を提供しています。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacksets-import.html

インポート処理をした場合にエラーになるパターンがいくつかあったので、その対処法をまとめます。

さきにまとめ

  • StackSetsとインポート対象スタックのテンプレートに差分がある場合
    1. StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する
    2. インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる
    3. インポート対象スタックを再度インポートする
  • インポート対象スタックがすでにスタックインスタンスとして存在する場合
    1. StackSetsのスタックインスタンスからインポート対象スタックインスタンスを削除する
    2. インポート対象スタックを再度インポートする
  • NoEchoを含むテンプレートはChangeSetで差分が検知されるため、インポート処理に失敗する

やってみる

前提として以下のテンプレートを展開するStackSetsを作成します。

example-stacksets.yaml
AWSTemplateFormatVersion: "2010-09-09"

Resources:
    S3Bucket:
        Type: "AWS::S3::Bucket"
        Properties:
            BucketName: !Sub "stackset-${AWS::AccountId}"
            PublicAccessBlockConfiguration:
                BlockPublicAcls: true
                BlockPublicPolicy: true
                IgnorePublicAcls: true
                RestrictPublicBuckets: true

Outputs:
    BucketName:
        Description: "Name of the created S3 bucket"
        Value: !Ref S3Bucket

インポートは以下の import-stacks-to-stack-set コマンドを使います。

aws cloudformation import-stacks-to-stack-set \
  --stack-set <スタックセット名> \
  --stack-ids "<既存アカウントスタックId 1>" "<既存アカウントスタックId 2>" ... \
  --organizational-unit-ids "<インポート対象先OUのUnitId>"

StackSetsとインポート対象スタックのテンプレートに差分がある場合

import-stacks-to-stack-set を使ってメンバーアカウントのスタックをインポートしてみます。

すると、以下のようなエラーが発生しました

01-1-cloudformation-stacksets-import-failure-troubleshooting

01-cloudformation-stacksets-import-failure-troubleshooting

ChangeSet: `StackImport-4ae0b90190f3565d9aa720f63e29bd98` contains changes: `[{Type: Resource,ResourceChange: {Action: Modify,LogicalResourceId: S3Bucket,PhysicalResourceId: stackset-73XXXXXXXX81,ResourceType: AWS::S3::Bucket,Replacement: False,Scope: [Properties],Details: [{Target: {Attribute: Properties,Name: Tags,RequiresRecreation: Never,},Evaluation: Static,ChangeSource: DirectModification,}],},ChangeAnalysisResults: []}]`

こちらはStackSetsのテンプレート内容とインポート対象スタックのテンプレート内容に差分がある場合に発生します。

エラー内容を見ていただくと、contains changesに差分内容が含まれていることがわかります。

[
  {
    Type: Resource,
    ResourceChange:
      {
        Action: Modify,
        LogicalResourceId: S3Bucket,
        PhysicalResourceId: stackset-73XXXXXXXX81,
        ResourceType: AWS::S3::Bucket,
        Replacement: False,
        Scope: [Properties],
        Details:
          [
            {
              Target: { Attribute: Properties, Name: Tags, RequiresRecreation: Never },
              Evaluation: Static,
              ChangeSource: DirectModification,
            },
          ],
      },
    ChangeAnalysisResults: [],
  },
]

対処法

こちらのエラーを解消するには、以下の手順を実施する必要があります。

  1. StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する
  2. インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる
  3. インポート対象スタックを再度インポートする

参考 : AWS CloudFormation StackSets のトラブルシューティング - AWS CloudFormation

では実際に対処していきたいと思います。

1. StackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除する

以下のコマンドでStackSetsのスタックインスタンスから失敗したインポート対象スタックインスタンスを削除します。

aws cloudformation delete-stack-instances \
  --stack-set <スタックセット名> \
  --deployment-targets 'OrganizationalUnitIds=["<インポート対象先OUのUnitId>"],Accounts=["<失敗したインポート対象アカウント>"],AccountFilterType=INTERSECTION' \
  --regions <リージョン> \
  --retain-stacks
実行コマンド
aws cloudformation delete-stack-instances \
  --stack-set example-stacksets \
  --deployment-targets 'OrganizationalUnitIds=["ou-0orw-bvsrcrpk"],Accounts=["73XXXXXXXX81"],AccountFilterType=INTERSECTION' \
  --regions ap-northeast-1 \
  --retain-stacks

削除オペレーションが完了すると、スタックインスタンスか失敗しているスタックインスタンスが削除されていることが確認できます。

02-cloudformation-stacksets-import-failure-troubleshooting

2. インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせる

今回インポート対象スタックのテンプレートは以下のようになっています。

Tagsの値が追加されており、StackSetsのテンプレートと差分があるためエラーが発生しています。

AWSTemplateFormatVersion: "2010-09-09"

Resources:
    S3Bucket:
        Type: "AWS::S3::Bucket"
        Properties:
            BucketName: !Sub "stackset-${AWS::AccountId}"
            PublicAccessBlockConfiguration:
                BlockPublicAcls: true
                BlockPublicPolicy: true
                IgnorePublicAcls: true
                RestrictPublicBuckets: true
            Tags:
                - Key: "Example"
                  Value: "Dummy"

Outputs:
    BucketName:
        Description: "Name of the created S3 bucket"
        Value: !Ref S3Bucket

こちらの差分を解消するためにインポート対象スタックが存在するアカウントでテンプレートを更新します。

StackSetsで展開されるCloudFormation名はStackSet-<スタックセット名>-<ランダムな文字列>がついています。

03-cloudformation-stacksets-import-failure-troubleshooting

3. インポート対象スタックを再度インポートする

インポート対象スタックのテンプレートをStackSetsのテンプレートに合わせた後、再度インポート処理を行います。

今度は問題なく、インポート対象スタックがインポートされることを確認できます。

04-cloudformation-stacksets-import-failure-troubleshooting

05-cloudformation-stacksets-import-failure-troubleshooting

小ネタ

エラーメッセージを見ていただくと分かる通り、差分のチェックはCloudFormationのChangeSetを使って行われています。

実際にインポート先アカウントを見てみると以下のようなChangeSetが作成されていることが確認できます。

この差分が発生している場合にはStackSetsはインポート処理を失敗させるようです。

06-cloudformation-stacksets-import-failure-troubleshooting

インポートが成功している場合には、以下のようにChangeSetに差分が存在しないことを表すエラーになります。

The submitted information didn't contain changes. Submit different information to create a change set.

07-cloudformation-stacksets-import-failure-troubleshooting

インポート対象スタックがすでにスタックインスタンスとして存在する場合

以下のようにすでにスタックインスタンスが存在する状態(SUCCEEDED/FAILED問わず)でインポート処理を行います。

01-cloudformation-stacksets-import-failure-troubleshooting

すでにスタックインスタンスが存在する状態でのインポート処理は以下のようにFAILEDとなります。

ただ、このときエラー内容は特に確認することができません。

08-cloudformation-stacksets-import-failure-troubleshooting

対処法

結論からいうと、この場合には一度スタックインスタンスを削除してからインポート処理を行う必要があります。

なので、テンプレート差分修正の手順で行ったようにスタックインスタンスを削除して再度インポート処理を実施してください。

  1. StackSetsのスタックインスタンスからインポート対象スタックインスタンスを削除する
  2. インポート対象スタックを再度インポートする

もし、ここで再度エラーが発生した場合にはテンプレート側の問題が考えられますので、テンプレートの修正を行ってください。

(余談)テンプレートにNoEchoが含まれる場合

最後にStackSetsでサポートされていないNoEchoを含むテンプレートのインポート処理をためしてみたいと思います。

NoEcho プロパティは StackSet インポートにサポートされていません。NoEcho を含むスタックは、StackSet のインポートを介して新しいスタックセットにインポートされません。

AWS CloudFormation StackSets へのスタックのインポート - AWS CloudFormation

テンプレートは以下のようにTagsのValueでNoEchoの値を使うようなものを用意しています

AWSTemplateFormatVersion: "2010-09-09"

Parameters:
    BucketTagValue:
        Type: String
        Description: "Name of the S3 bucket"
        NoEcho: true

Resources:
    S3Bucket:
        Type: "AWS::S3::Bucket"
        Properties:
            BucketName: !Sub "stackset-${AWS::AccountId}"
            PublicAccessBlockConfiguration:
                BlockPublicAcls: true
                BlockPublicPolicy: true
                IgnorePublicAcls: true
                RestrictPublicBuckets: true
            Tags:
                - Key: "Example"
                  Value: !Ref BucketTagValue

Outputs:
    BucketName:
        Description: "Name of the created S3 bucket"
        Value: !Ref S3Bucket

こちらでインポート処理を行うと以下のようなエラーが発生しました。

09-cloudformation-stacksets-import-failure-troubleshooting

「StackSetsとインポート対象スタックのテンプレートに差分がある場合」のパターンと同様にテンプレート内容に差分を検知してインポートに失敗しているようです。

インポート対象アカウントのスタックを確認すると以下のようなChangeSetが作成されていることが確認できます。

10-cloudformation-stacksets-import-failure-troubleshooting

"Value": "****"が検知されるようにNoEchoの値がChangeSetとして検知されるため、StackSet インポートではサポートされないようですね。

最後に

今回はCloudFormation StackSetsのインポート処理が失敗する場合の対処法についてまとめました。

エラーメッセージが出てくれるものは対処法が明確ですが、「インポート対象スタックがすでにスタックインスタンスとして存在する場合」のようにエラー内容がなくハマってしまったで今回の記事を書いてみました。

こちらの内容がどなたかのお役に立てれば幸いです。

以上、たかやま(@nyan_kotaroo)でした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.